﻿using System;
using System.Collections.Generic;
using System.Text;

namespace Vortex.Drawing {

	//Contains methods for drawing primitives
	public sealed partial class Canvas2D {
		private const int MAX_ATOMIC_POLYGON_LENGTH = 32768;

		#region Lines

		/// <summary>
		/// Draws the simple 1 px line.
		/// </summary>
		/// <param name="startX">The start X coord of line.</param>
		/// <param name="startY">The start Y coord of line.</param>
		/// <param name="endX">The end X coord of line.</param>
		/// <param name="endY">The end Y coord of line.</param>
		/// <param name="colorTint">The color tint.</param>
		public void DrawLine(float startX, float startY, float endX, float endY, ColorU colorTint) {
			DrawLine(new Vector2(startX, startY), new Vector2(endX, endY), colorTint);
		}

		/// <summary>
		/// Draws the simple 1px line.
		/// </summary>
		/// <param name="start">The start point.</param>
		/// <param name="end">The end point.</param>
		/// <param name="colorTint">The color tint.</param>
		public void DrawLine(Vector2 start, Vector2 end, ColorU color) {
			_renderer.SetCanvas(this, null);

			FragmentLocation location;
			_renderer.AllocateFragment(VertexFragment.Line, out location);

			unsafe {
				Vertex2D* vertices = location.Vertices;
				//---------------------------------------------------
				vertices[0].XY = new Vector2(
					_transform.M11 * start.X + _transform.M12 * start.Y + _transform.TX + 0.5f,
					_transform.M21 * start.X + _transform.M22 * start.Y + _transform.TY + 0.5f
				);
				vertices[0].Color = color;
				vertices[0].Z = _Z;
				//---------------------------------------------------
				vertices[1].XY = new Vector2(
					_transform.M11 * end.X + _transform.M12 * end.Y + _transform.TX + 0.5f,
					_transform.M21 * end.X + _transform.M22 * end.Y + _transform.TY + 0.5f
				);
				vertices[1].Color = color;
				vertices[1].Z = _Z;
				//---------------------------------------------------
				//populate indices
				location.Indices[0] = location.BaseIndex;
				location.Indices[1] = (ushort)(location.BaseIndex + 1);
			}
		}

		#endregion

		#region Rectangle

		/// <summary>
		/// Draws the rect.
		/// </summary>
		/// <param name="x">The x of left top rect corner.</param>
		/// <param name="y">The y of left top rect cornder.</param>
		/// <param name="width">The width of rect.</param>
		/// <param name="height">The height of rect.</param>
		/// <param name="colorTint">The color tint.</param>
		public void DrawRect(float x, float y, float width, float height, ColorU colorTint) {
			DrawRect(Rect.FromBox(x, y, width, height), colorTint);
		}

		/// <summary>
		/// Draws the solid simple rectangle.
		/// </summary>
		/// <param name="rect">The rectangle.</param>
		/// <param name="color">The color tint.</param>
		public void DrawRect(Rect rect, ColorU colorTint) {
			_renderer.SetCanvas(this, null);

			//for rectangle we need fix width/height
			rect.Right -= 1;
			rect.Bottom -= 1;

			FragmentLocation location;
			_renderer.AllocateFragment(VertexFragment.Rect, out location);

			unsafe {
				Vertex2D* vertices = location.Vertices;
				//---------------------------------------------------
				vertices[0].XY = new Vector2(
					_transform.M11 * rect.Left + _transform.M12 * rect.Top + _transform.TX + 0.5f,
					_transform.M21 * rect.Left + _transform.M22 * rect.Top + _transform.TY + 0.5f
				);
				vertices[0].Z = _Z;
				vertices[0].Color = colorTint;
				//---------------------------------------------------
				vertices[1].XY = new Vector2(
					_transform.M11 * rect.Right + _transform.M12 * rect.Top + _transform.TX + 0.5f,
					_transform.M21 * rect.Right + _transform.M22 * rect.Top + _transform.TY + 0.5f
				);
				vertices[1].Z = _Z;
				vertices[1].Color = colorTint;
				//---------------------------------------------------
				vertices[2].XY = new Vector2(
					_transform.M11 * rect.Right + _transform.M12 * rect.Bottom + _transform.TX + 0.5f,
					_transform.M21 * rect.Right + _transform.M22 * rect.Bottom + _transform.TY + 0.5f
				);
				vertices[2].Z = _Z;
				vertices[2].Color = colorTint;
				//---------------------------------------------------
				vertices[3].XY = new Vector2(
					_transform.M11 * rect.Left + _transform.M12 * rect.Bottom + _transform.TX + 0.5f,
					_transform.M21 * rect.Left + _transform.M22 * rect.Bottom + _transform.TY + 0.5f
				);
				vertices[3].Z = _Z;
				vertices[3].Color = colorTint;

				//populate indices
				location.Indices[0] = location.BaseIndex;
				location.Indices[1] = (ushort)(location.BaseIndex + 1);
				location.Indices[2] = (ushort)(location.BaseIndex + 1);
				location.Indices[3] = (ushort)(location.BaseIndex + 2);
				location.Indices[4] = (ushort)(location.BaseIndex + 2);
				location.Indices[5] = (ushort)(location.BaseIndex + 3);
				location.Indices[6] = (ushort)(location.BaseIndex + 3);
				location.Indices[7] = (ushort)(location.BaseIndex + 0);
			}
		}

		/// <summary>
		/// Draws the solid filled rectangle.
		/// </summary>
		/// <param name="region">The rectangle to fill.</param>
		/// <param name="colorTint">The color tint.</param>
		public void DrawFilledRect(Rect region, ColorU colorTint) {
			_renderer.SetCanvas(this, null);

			FragmentLocation location;
			_renderer.AllocateFragment(VertexFragment.Quad, out location);

			unsafe {				
				Vertex2D* vertices = location.Vertices;
				vertices[0].XY = region.LeftTop;
				vertices[0].Color = colorTint;
				vertices[0].UV1 = new Vector2(0, 0);
				vertices[0].Z = _Z;

				vertices[1].XY = region.RightTop;
				vertices[1].Color = colorTint;
				vertices[1].UV2 = new Vector2(1, 0);
				vertices[1].Z = _Z;

				vertices[2].XY = region.RightBottom;
				vertices[2].Color = colorTint;
				vertices[2].UV1 = new Vector2(1, 1);
				vertices[2].Z = _Z;

				vertices[3].XY = region.LeftBottom;
				vertices[3].Color = colorTint;
				vertices[3].UV1 = new Vector2(0, 1);
				vertices[3].Z = _Z;
				
				if (null != _currentBrush) {
					FillBrushCoords(vertices);
				}

				//apply transform
				Transform4Vertices(ref _transform, vertices);

				//populate indices
				PopulateQuadIndices(ref location);
				
			}
		}

		/// <summary>
		/// Draws the filled rect.
		/// </summary>
		/// <param name="x">The x of left top rect point.</param>
		/// <param name="y">The y of left top rect point.</param>
		/// <param name="width">The width of rect.</param>
		/// <param name="height">The height of rect.</param>
		/// <param name="colorTint">The color tint.</param>
		public void DrawFilledRect(float x, float y, float width, float height, ColorU colorTint) {
			DrawFilledRect(new Rect(x, y, x + width, y + height), colorTint);
		}

		#endregion

		#region Draw Ellipse


		/// <summary>
		/// Draws the ellipse.
		/// </summary>
		/// <param name="region">The region to draw ellipse.</param>
		/// <param name="colorTint">The color tint.</param>
		public void DrawEllipse(Rect region, ColorU colorTint) {
			_renderer.SetCanvas(this, null);

			FragmentLocation location;
			_renderer.AllocateFragment(EllipseDrawInfo.EllipseFragment, out location);

			unsafe {				
				Vector2[] offsets = EllipseDrawInfo.EllipseSegments;
				Vector2 corner = region.LeftTop;
				Vector2 size = region.Size;
				int lastOffset = offsets.Length - 1;

				Vertex2D* vertices = location.Vertices;
				ushort* indices = location.Indices;
				ushort arcBaseIndex = location.BaseIndex;

				for (int n = 0; n < offsets.Length; ++n) {
					vertices->XY = offsets[n] * size + corner;
					vertices->Z = _Z;
					vertices->Color = colorTint;
					vertices++;

					indices[0] = (ushort)(arcBaseIndex + n);
					indices[1] = (ushort)(arcBaseIndex + ((n == lastOffset) ? 0 : (n + 1)));
					indices += 2;
				}

				//transform everything
				TransformVertices(ref _transform, offsets.Length, location.Vertices);
			}
		}

		/// <summary>
		/// Draws the ellipse.
		/// </summary>
		/// <param name="x">The x of left top point of rectangular region.</param>
		/// <param name="y">The y of left top point of rectangular region..</param>
		/// <param name="width">The width of rectangular region.</param>
		/// <param name="height">The height of rectangular region.</param>
		/// <param name="colorTint">The color tint.</param>
		public void DrawEllipse(float x, float y, float width, float height, ColorU colorTint) {
			DrawEllipse(new Rect(x, y, x + width, y + height), colorTint);
		}

		/// <summary>
		/// Draws the solid filled ellipse.
		/// </summary>
		/// <param name="region">The region to draw ellipse.</param>
		/// <param name="colorTint">The color tint.</param>
		public void DrawFilledEllipse(Rect region, ColorU colorTint) {
			_renderer.SetCanvas(this, null);

			FragmentLocation location;
			_renderer.AllocateFragment(EllipseDrawInfo.FilledEllipseFragment, out location);

			unsafe {				
				Vector2[] offsets = EllipseDrawInfo.EllipseSegments;
				Vector2 offset = region.LeftTop;
				Vector2 size = region.Size;

				Rect coordRegion = null != _currentBrush ? _currentBrush.TransformedRegion : Rect.Unit;
				Vector2 coordsOffset = coordRegion.LeftTop;
				Vector2 coordsSize = coordRegion.Size;
				
				int lastOffset = offsets.Length - 1;

				Vertex2D* vertices = location.Vertices;
				ushort* indices = location.Indices;
				ushort arcBaseIndex = (ushort)(location.BaseIndex + 1);

				//setup central point
				vertices[0].XY = region.Center;
				vertices[0].Z = _Z;
				vertices[0].Color = colorTint;
				vertices[0].UV1 = Vector2.Unit.Half;
				vertices[0].UV2 = coordRegion.Center;
				vertices++;

				for (int n = 0; n < offsets.Length; ++n) {
					vertices[n].XY = offsets[n] * size + offset;
					vertices[n].Z = _Z;
					vertices[n].Color = colorTint;			
					vertices[n].UV1 = offsets[n];
					vertices[n].UV2 = offsets[n] * coordsSize + coordsOffset;

					indices[0] = (ushort)(arcBaseIndex + n);
					indices[1] = (ushort)(arcBaseIndex + ((n == lastOffset) ? 0 : (n + 1)));
					indices[2] = location.BaseIndex;
					indices += 3;
				}

				TransformVertices(ref _transform, (int)EllipseDrawInfo.FilledEllipseFragment.VertexCount, location.Vertices, new Vector2(0.5f, 0.5f));
			}
		}

		/// <summary>
		/// Draws the solid filled ellipse.
		/// </summary>
		/// <param name="x">The x of left top point of rectangular region.</param>
		/// <param name="y">The y of left top point of rectangular region..</param>
		/// <param name="width">The width of rectangular region.</param>
		/// <param name="height">The height of rectangular region.</param>
		/// <param name="colorTint">The color tint.</param>
		public void DrawFilledEllipse(float x, float y, float width, float height, ColorU colorTint) {
			DrawFilledEllipse(new Rect(x, y, x + width, y + height), colorTint);
		}


		/// <summary>
		/// Draws the polyline.
		/// </summary>
		/// <param name="points">The array with points points of polyline.</param>
		/// <param name="colorTint">The color tint.</param>
		public void DrawPolyline(Vector2[] points, ColorU colorTint) {
			DrawPolyline(points, 0, -1, colorTint);
		}

		/// <summary>
		/// Draws the polyline.
		/// </summary>
		/// <param name="points">The array with points of polyline.</param>
		/// <param name="startElement">The start element in array to use.</param>
		/// <param name="elementCount">The element count to use.</param>
		/// <param name="colorTint">The color tint.</param>
		public void DrawPolyline(Vector2[] points, int startElement, int elementCount, ColorU colorTint) {
			DrawPolyline(points, startElement, elementCount, colorTint, false);
		}

		/// <summary>
		/// Draws the polygon.
		/// </summary>
		/// <param name="points">The array with points points of polyline.</param>
		/// <param name="colorTint">The color tint.</param>
		public void DrawPolygon(Vector2[] points, ColorU colorTint) {
			DrawPolygon(points, 0, -1, colorTint);
		}

		/// <summary>
		/// Draws the polygon.
		/// </summary>
		/// <param name="points">The array with points of polyline.</param>
		/// <param name="startElement">The start element in array to use.</param>
		/// <param name="elementCount">The element count to use.</param>
		/// <param name="colorTint">The color tint.</param>
		public void DrawPolygon(Vector2[] points, int startElement, int elementCount, ColorU colorTint) {
			DrawPolyline(points, startElement, elementCount, colorTint, true);
		}

		#endregion

		/// <summary>
		/// Draws the polyline.
		/// </summary>
		/// <param name="points">The array with points of polyline.</param>
		/// <param name="startElement">The start element in array to use.</param>
		/// <param name="elementCount">The element count to use.</param>
		/// <param name="colorTint">The color tint.</param>
		/// <param name="connectLastWithFirst">if set to <c>true</c> to connect last point with the first one.</param>
		private void DrawPolyline(Vector2[] points, int startElement, int elementCount, ColorU colorTint, bool connectLastWithFirst) {
			if (points == null) throw new ArgumentNullException("points");

			if (startElement < 0 || startElement >= points.Length) {
				throw new ArgumentException("startElement", "Start element is out of array index");
			}

			if (elementCount == -1) {
				elementCount = points.Length - startElement;
			} else
				if (startElement + elementCount > points.Length) {
					throw new ArgumentException("elementCount", "End element is out of array index");
				}

			if (elementCount > 1) {
				if (elementCount <= MAX_ATOMIC_POLYGON_LENGTH) {
					_renderer.SetCanvas(this, null);

					int vertexCount = elementCount, lineCount = elementCount - (connectLastWithFirst ? 0: 1);

					unsafe {
						VertexFragment fragment = new VertexFragment(vertexCount, lineCount * 2, PrimitiveType.LineList);
						FragmentLocation location;
						_renderer.AllocateFragment(fragment, out location);

						Vertex2D* vertices = location.Vertices;
						ushort* indices = location.Indices;
						ushort index = location.BaseIndex;

						//populate core line vertices
						for (int n = 0; n < vertexCount; ++n) {
							Transform2D.Multiply(ref _transform, ref points[n + startElement], out vertices->XY);
							//fix line alignment issue
							vertices->XY += new Vector2(0.5f, 0.5f);
							vertices->Z = _Z;
							vertices->Color = colorTint;

							if (n < vertexCount - 1) {
								indices[0] = (ushort)index;
								indices[1] = (ushort)(index + 1);
								indices += 2;
								index++;
							} else {
								//if need to connect last and first - lets do it
								if (connectLastWithFirst) {
									indices[0] = index;
									indices[1] = location.BaseIndex;
								}
							}

							/*
							vertices->x += 0.5f;
							vertices->y += 0.5f;
							*/

							vertices++;
						}
					}
				} else {
					int step = MAX_ATOMIC_POLYGON_LENGTH - 1;
					//split polygon/polyline into less parts
					for (int n = 0; n < elementCount; n += step) {
						DrawPolyline(points, startElement + n - (n != 0 ? 1 : 0), Math.Min(step, elementCount - n) + (n != 0 ? 1 : 0), colorTint, false);
					}
					if (connectLastWithFirst) {
						DrawPolyline(new Vector2[] { points[startElement + elementCount - 1], points[startElement] }, 0, 2, colorTint, false);
					}

				}		
			} else {
				throw new ArgumentException("Drawing polyline/polygon required at least 2 points");
			}
		}

		#region Draw Primitives

		/// <summary>
		/// Draws the triangle from specified vertices.
		/// </summary>
		/// <param name="texture">The texture to use for triangle shading. May be null (blank texture).</param>
		/// <param name="vertex1">The vertex1 reference.</param>
		/// <param name="vertex2">The vertex2 reference.</param>
		/// <param name="vertex3">The vertex3 reference.</param>
		/// <param name="flags">Additional flags which specifies some aspects of vertex component processing. See <see cref="Vortex.Drawing.DrawingFlags"/> for options.</param>
		public void DrawTriangle(BaseTexture texture, ref Vertex2D vertex1, ref Vertex2D vertex2, ref Vertex2D vertex3, DrawingFlags flags) {
			_renderer.SetCanvas(this, texture);

			FragmentLocation location;
			_renderer.AllocateFragment(VertexFragment.Triangle, out location);

			unsafe {
				Vertex2D* vertices = location.Vertices;
				ushort* indices = location.Indices;
				int baseIndex = location.BaseIndex;
				
				//copy vertices XY
				if ((flags & DrawingFlags.SkipXYTransform) == DrawingFlags.SkipXYTransform) {
					vertices[0].XY = vertex1.XY;
					vertices[1].XY = vertex2.XY;
					vertices[2].XY = vertex3.XY;
				} else {
					Transform2D.Multiply(ref _transform, ref vertex1.XY, out vertices[0].XY);
					Transform2D.Multiply(ref _transform, ref vertex2.XY, out vertices[1].XY);
					Transform2D.Multiply(ref _transform, ref vertex3.XY, out vertices[2].XY);
				}

				//copy vertices Z
				if ((flags & DrawingFlags.ForceCanvasZ) == DrawingFlags.ForceCanvasZ) {
					location.Vertices[0].Z = location.Vertices[1].Z = location.Vertices[2].Z = _Z;
				} else {
					location.Vertices[0].Z = vertex1.Z;
					location.Vertices[1].Z = vertex2.Z;
					location.Vertices[2].Z = vertex3.Z;
				}

				//copy color
				location.Vertices[0].Color = vertex1.Color;
				location.Vertices[1].Color = vertex2.Color;
				location.Vertices[2].Color = vertex3.Color;

				//copy UV 1
				if (null != texture && (flags & DrawingFlags.TransformUV1) == DrawingFlags.TransformUV1) {
					texture.Transform(ref vertex1.UV1, out vertices[0].UV1);
					texture.Transform(ref vertex2.UV1, out vertices[1].UV1);
					texture.Transform(ref vertex3.UV1, out vertices[2].UV1);
				} else {
					location.Vertices[0].UV1 = vertex1.UV1;
					location.Vertices[1].UV1 = vertex2.UV1;
					location.Vertices[2].UV1 = vertex3.UV1;
				}

				//copy UV 2
				BaseTexture brushTexture = _states.BrushTexture;
				if (null != brushTexture && (flags & DrawingFlags.TransformUV2) == DrawingFlags.TransformUV2) {
					texture.Transform(ref vertex1.UV2, out vertices[0].UV2);
					texture.Transform(ref vertex2.UV2, out vertices[1].UV2);
					texture.Transform(ref vertex3.UV2, out vertices[2].UV2);
				} else {
					location.Vertices[0].UV2 = vertex1.UV2;
					location.Vertices[1].UV2 = vertex2.UV2;
					location.Vertices[2].UV2 = vertex3.UV2;
				}

				//add indices
				indices[0] = (ushort)(baseIndex + 0);
				indices[1] = (ushort)(baseIndex + 1);
				indices[2] = (ushort)(baseIndex + 2);
			}
		}

		/// <summary>
		/// Draws the quad from with two triangles. Vertices are CW and starts from left-top one.
		/// </summary>
		/// <param name="texture">The texture to use for quad shading. May be null (blank texture).</param>
		/// <param name="vertex1">The vertex1 reference.</param>
		/// <param name="vertex2">The vertex2 reference.</param>
		/// <param name="vertex3">The vertex3 reference.</param>
		/// <param name="vertex4">The vertex4 reference.</param>
		/// <param name="flags">Additional flags which specifies some aspects of vertex component processing. See <see cref="Vortex.Drawing.DrawingFlags"/> for options.</param>
		public void DrawQuad(BaseTexture texture, ref Vertex2D vertex1, ref Vertex2D vertex2, ref Vertex2D vertex3, ref Vertex2D vertex4, DrawingFlags flags) {
			_renderer.SetCanvas(this, texture);

			FragmentLocation location;
			_renderer.AllocateFragment(VertexFragment.Quad, out location);

			unsafe {
				Vertex2D* vertices = location.Vertices;
				ushort* indices = location.Indices;
				int baseIndex = location.BaseIndex;

				//copy vertices XY
				if ((flags & DrawingFlags.SkipXYTransform) == DrawingFlags.SkipXYTransform) {
					vertices[0].XY = vertex1.XY;
					vertices[1].XY = vertex2.XY;
					vertices[2].XY = vertex3.XY;
					vertices[3].XY = vertex4.XY;
				} else {
					Transform2D.Multiply(ref _transform, ref vertex1.XY, out vertices[0].XY);
					Transform2D.Multiply(ref _transform, ref vertex2.XY, out vertices[1].XY);
					Transform2D.Multiply(ref _transform, ref vertex3.XY, out vertices[2].XY);
					Transform2D.Multiply(ref _transform, ref vertex4.XY, out vertices[3].XY);
				}

				//copy vertices Z
				if ((flags & DrawingFlags.ForceCanvasZ) == DrawingFlags.ForceCanvasZ) {
					location.Vertices[0].Z = location.Vertices[1].Z = location.Vertices[2].Z = location.Vertices[3].Z = _Z;
				} else {
					location.Vertices[0].Z = vertex1.Z;
					location.Vertices[1].Z = vertex2.Z;
					location.Vertices[2].Z = vertex3.Z;
				}

				//copy color
				location.Vertices[0].Color = vertex1.Color;
				location.Vertices[1].Color = vertex2.Color;
				location.Vertices[2].Color = vertex3.Color;
				location.Vertices[3].Color = vertex4.Color;

				//copy UV 1
				if (null != texture && (flags & DrawingFlags.TransformUV1) == DrawingFlags.TransformUV1) {
					texture.Transform(ref vertex1.UV1, out vertices[0].UV1);
					texture.Transform(ref vertex2.UV1, out vertices[1].UV1);
					texture.Transform(ref vertex3.UV1, out vertices[2].UV1);
					texture.Transform(ref vertex4.UV1, out vertices[3].UV1);
				} else {
					location.Vertices[0].UV1 = vertex1.UV1;
					location.Vertices[1].UV1 = vertex2.UV1;
					location.Vertices[2].UV1 = vertex3.UV1;
					location.Vertices[3].UV1 = vertex4.UV1;
				}

				//copy UV 2
				BaseTexture brushTexture = _states.BrushTexture;
				if (null != brushTexture && (flags & DrawingFlags.TransformUV2) == DrawingFlags.TransformUV2) {
					texture.Transform(ref vertex1.UV2, out vertices[0].UV2);
					texture.Transform(ref vertex2.UV2, out vertices[1].UV2);
					texture.Transform(ref vertex3.UV2, out vertices[2].UV2);
					texture.Transform(ref vertex4.UV2, out vertices[3].UV2);
				} else {
					location.Vertices[0].UV2 = vertex1.UV2;
					location.Vertices[1].UV2 = vertex2.UV2;
					location.Vertices[2].UV2 = vertex3.UV2;
					location.Vertices[3].UV2 = vertex4.UV2;
				}

				//add indices
				indices[0] = (ushort)(baseIndex + 0);
				indices[1] = (ushort)(baseIndex + 1);
				indices[2] = (ushort)(baseIndex + 2);
				indices[3] = (ushort)(baseIndex + 0);
				indices[4] = (ushort)(baseIndex + 2);
				indices[5] = (ushort)(baseIndex + 3);

			}
		}

		/// <summary>
		/// Draws primitives from specified vertex array.
		/// </summary>
		/// <param name="texture">The texture to use for primitive shading. May be null (blank texture)s.</param>
		/// <param name="vertices">The vertex array.</param>
		/// <param name="primitiveType">Type of the primitives to draw from vertices.</param>
		/// <param name="flags">Additional flags which specifies some aspects of vertex component processing. See <see cref="Vortex.Drawing.DrawingFlags"/> for options.</param>
		public void DrawPrimitives(BaseTexture texture, Vertex2D[] vertices, PrimitiveType primitiveType, DrawingFlags flags) {
			DrawPrimitives(texture, vertices, 0, -1, primitiveType, flags);
		}

		/// <summary>
		/// Draws primitives from specified vertex array.
		/// </summary>
		/// <param name="texture">The texture to use for primitive shading. May be null (blank texture)s.</param>
		/// <param name="vertices">The vertex array.</param>
		/// <param name="startVertex">The start vertex in array.</param>
		/// <param name="vertexCount">The vertex count to draw. If vertex count == -1 then vertexCount = vertices.Length - startVertex</param>
		/// <param name="primitiveType">Type of the primitives to draw from vertices.</param>
		/// <param name="flags">Additional flags which specifies some aspects of vertex component processing. See <see cref="Vortex.Drawing.DrawingFlags"/> for options.</param>
		public void DrawPrimitives(BaseTexture texture, Vertex2D[] vertices, int startVertex, int vertexCount, PrimitiveType primitiveType, DrawingFlags flags) {			
			if (null == vertices) throw new ArgumentNullException("vertices");
			if (vertexCount == -1) vertexCount = vertices.Length - startVertex;
			if (vertexCount == 0) throw new ArgumentException("Empty drawing vertex array", "vertexCount");
			if (startVertex < 0 || vertexCount < 0 || startVertex + vertexCount > vertices.Length) throw new ArgumentOutOfRangeException(string.Format("User specified vertex range {0}..{1} is out of specified array range 0..{2}", startVertex, startVertex + vertexCount, vertices.Length - 1));

			_renderer.SetCanvas(this, texture);

			if (primitiveType == PrimitiveType.Undefined) throw new ArgumentException("Unable to draw unknown type of primitives", "primitiveType");
			if (!CheckVertexCountAndPrimitiveType(vertexCount, primitiveType)) {
				throw new ArgumentException(string.Format("Unable to draw {0} from {1} vertices ", primitiveType, vertexCount), "primitiveType");
			}

			FragmentLocation location;
			_renderer.AllocateFragment(new VertexFragment(vertexCount, vertexCount, primitiveType), out location);

			unsafe {
				CopyVertexArrayToFragment(texture, _states.BrushTexture, vertices, startVertex, vertexCount, location.Vertices, flags);

				//make indices
				for (int n = 0; n < vertexCount; ++n) {
					location.Indices[n] = (ushort)(location.BaseIndex + n);
				}
			}
		}

		/// <summary>
		/// Draws indexed primitives from specified vertex/index array.
		/// </summary>
		/// <param name="texture">The texture to use for primitive shading. May be null (blank texture)s.</param>
		/// <param name="vertices">The vertex array.</param>
		/// <param name="indices">The index array. Indexes are relative to first vertex in vertex array range. Maximal value of index is 65535.</param>
		/// <param name="primitiveType">Type of the primitives to draw from vertices.</param>
		/// <param name="flags">Additional flags which specifies some aspects of vertex component processing. See <see cref="Vortex.Drawing.DrawingFlags"/> for options.</param>
		public void DrawIndexedPrimitives(BaseTexture texture, Vertex2D[] vertices, int[] indices, PrimitiveType primitiveType, DrawingFlags flags) {
			DrawIndexedPrimitives(texture, vertices, 0, -1, indices, 0, -1, primitiveType, flags);
		}

		/// <summary>
		/// Draws indexed primitives from specified vertex/index array.
		/// </summary>
		/// <param name="texture">The texture to use for primitive shading. May be null (blank texture)s.</param>
		/// <param name="vertices">The vertex array.</param>
		/// <param name="startVertex">The start vertex in array.</param>
		/// <param name="vertexCount">The vertex count to draw. If vertex count == -1 then vertexCount = vertices.Length - startVertex</param>
		/// <param name="indices">The index array. Indexes are relative to first vertex in vertex array range. Maximal value of index is 65535.</param>
		/// <param name="startIndex">The first number of index in array.</param>
		/// <param name="indexCount">The index count. If index count == -1 then indexCount = indices.Length - startIndex</param>
		/// <param name="primitiveType">Type of the primitives to draw from vertices.</param>
		/// <param name="flags">Additional flags which specifies some aspects of vertex component processing. See <see cref="Vortex.Drawing.DrawingFlags"/> for options.</param>
		public void DrawIndexedPrimitives(BaseTexture texture, Vertex2D[] vertices, int startVertex, int vertexCount, int[] indices, PrimitiveType primitiveType, DrawingFlags flags) {
			DrawIndexedPrimitives(texture, vertices, startVertex, vertexCount, indices, 0, -1, primitiveType, flags);
		}

		/// <summary>
		/// Draws indexed primitives from specified vertex/index array.
		/// </summary>
		/// <param name="texture">The texture to use for primitive shading. May be null (blank texture)s.</param>
		/// <param name="vertices">The vertex array.</param>
		/// <param name="indices">The index array. Indexes are relative to first vertex in vertex array range. Maximal value of index is 65535.</param>
		/// <param name="startIndex">The first number of index in array.</param>
		/// <param name="indexCount">The index count. If index count == -1 then indexCount = indices.Length - startIndex</param>
		/// <param name="primitiveType">Type of the primitives to draw from vertices.</param>
		/// <param name="flags">Additional flags which specifies some aspects of vertex component processing. See <see cref="Vortex.Drawing.DrawingFlags"/> for options.</param>
		public void DrawIndexedPrimitives(BaseTexture texture, Vertex2D[] vertices, int[] indices, int startIndex, int indexCount, PrimitiveType primitiveType, DrawingFlags flags) {
			DrawIndexedPrimitives(texture, vertices, 0, -1, indices, startIndex, indexCount, primitiveType, flags);
		}

		/// <summary>
		/// Draws indexed primitives from specified vertex/index array.
		/// </summary>
		/// <param name="texture">The texture to use for primitive shading. May be null (blank texture)s.</param>
		/// <param name="vertices">The vertex array.</param>
		/// <param name="startVertex">The start vertex in array.</param>
		/// <param name="vertexCount">The vertex count to draw. If vertex count == -1 then vertexCount = vertices.Length - startVertex</param>
		/// <param name="indices">The index array. Indexes are relative to first vertex in vertex array range. Maximal value of index is 65535.</param>
		/// <param name="startIndex">The first number of index in array.</param>
		/// <param name="indexCount">The index count. If index count == -1 then indexCount = indices.Length - startIndex</param>
		/// <param name="primitiveType">Type of the primitives to draw from vertices.</param>
		/// <param name="flags">Additional flags which specifies some aspects of vertex component processing. See <see cref="Vortex.Drawing.DrawingFlags"/> for options.</param>
		public void DrawIndexedPrimitives(BaseTexture texture, Vertex2D[] vertices, int startVertex, int vertexCount, int[] indices, int startIndex, int indexCount, PrimitiveType primitiveType, DrawingFlags flags) {
			if (null == vertices) throw new ArgumentNullException("vertices");
			if (null == indices) throw new ArgumentNullException("indices");
			if (vertexCount == -1) vertexCount = vertices.Length - startVertex;
			if (indexCount == -1) indexCount = indices.Length - startIndex;
			if (vertexCount == 0) throw new ArgumentException("Empty drawing vertex array", "vertexCount");
			if (indexCount == 0) throw new ArgumentException("Empty drawing index array", "indexCount");
			if (startVertex < 0 || vertexCount < 0 || startVertex + vertexCount > vertices.Length) throw new ArgumentOutOfRangeException(string.Format("User specified vertex range {0}..{1} is out of specified array range 0..{2}", startVertex, startVertex + vertexCount - 1, vertices.Length - 1));
			if (startIndex < 0 || indexCount < 0 || startIndex + indexCount > indices.Length) throw new ArgumentOutOfRangeException(string.Format("User specified index range {0}..{1} is out of specified array range 0..{2}", startIndex, startIndex + indexCount - 1, indices.Length - 1));

			_renderer.SetCanvas(this, texture);

			if (primitiveType == PrimitiveType.Undefined) throw new ArgumentException("Unable to draw unknown type of primitives", "primitiveType");
			if (!CheckVertexCountAndPrimitiveType(indexCount, primitiveType)) {
				throw new ArgumentException(string.Format("Unable to draw {0} from {1} vertices ", primitiveType, vertexCount), "primitiveType");
			}

			FragmentLocation location;
			_renderer.AllocateFragment(new VertexFragment(vertexCount, indexCount, primitiveType), out location);

			unsafe {
				CopyVertexArrayToFragment(texture, _states.BrushTexture, vertices, startVertex, vertexCount, location.Vertices, flags);

				//make indices
				for (int n = 0; n < indexCount; ++n) {
					location.Indices[n] = (ushort)(location.BaseIndex + indices[startIndex + n]);
				}
			}
		}

		/// <summary>
		/// Copies the part of vertex array to vertex fragment with all of user specified processing.
		/// </summary>
		/// <param name="texture1">The texture N1.</param>
		/// <param name="texture2">The texture N2.</param>
		/// <param name="source">The source array.</param>
		/// <param name="startVertex">The start vertex.</param>
		/// <param name="vertexCount">The vertex count. If vertex count == -1 then vertexCount = vertices.Length - startVertex</param>
		/// <param name="target">The target vertex pointer.</param>
		/// <param name="flags">Additional flags which specifies some aspects of vertex component processing. See <see cref="Vortex.Drawing.DrawingFlags"/> for options.</param>
		private unsafe void CopyVertexArrayToFragment(BaseTexture texture1, BaseTexture texture2, Vertex2D[] source, int startVertex, int vertexCount, Vertex2D* target, DrawingFlags flags) {
			int endVertex = startVertex + vertexCount;
			for (int n = startVertex; n < endVertex; ++n) {
				//copy vertices XY
				if ((flags & DrawingFlags.SkipXYTransform) == DrawingFlags.SkipXYTransform) {
					target[n].XY = source[n].XY;
				} else {
					Transform2D.Multiply(ref _transform, ref source[n].XY, out target[n].XY);
				}

				//copy vertices Z
				if ((flags & DrawingFlags.ForceCanvasZ) == DrawingFlags.ForceCanvasZ) {
					target[n].Z = _Z;
				} else {
					target[n].Z = source[n].Z;
				}

				//copy color
				target[n].Color = source[n].Color;

				//copy UV 1
				if (null != texture1 && (flags & DrawingFlags.TransformUV1) == DrawingFlags.TransformUV1) {
					texture1.Transform(ref source[n].UV1, out target[n].UV1);
				} else {
					target[n].UV1 = source[n].UV1;
				}

				//copy UV 2
				if (null != texture2 && (flags & DrawingFlags.TransformUV2) == DrawingFlags.TransformUV2) {
					texture2.Transform(ref source[n].UV2, out target[n].UV2);
				} else {
					target[n].UV2 = source[n].UV2;
				}
			}
		}

		/// <summary>
		/// Checks the type of primitive and the given vertex count.
		/// Most important rules, line list should be divisible 2, triangle list - divisible, strips and fans should be greater 2 for line and 3 for triangles.
		/// </summary>
		/// <param name="vertexCount">The vertex count.</param>
		/// <param name="primitiveType">Type of the primitive.</param>
		/// <returns><c>true</c> if vertex count fits with specified primitive type</returns>
		private bool CheckVertexCountAndPrimitiveType(int vertexCount, PrimitiveType primitiveType) {
			switch (primitiveType) {
				case PrimitiveType.LineList:
					return vertexCount % 2 == 0;
				case PrimitiveType.ListStrip:
					return vertexCount >= 2;
				case PrimitiveType.TriangleList:
					return vertexCount % 3 == 0;
				case PrimitiveType.TriangleStrip:
				case PrimitiveType.TriangleFan:
					return vertexCount >= 3;
				default:
					return true;
			}
		}

		#endregion
	}
}
